// Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{error::Error, ffi::{c_char, c_void}};

use crate::node::{Node, NodeKind};

// Wrapper for FFI callback data that can be sent across threads
// SAFETY: This is FFI code where the caller is responsible for thread safety
struct CallbackData {
    callback: extern "C" fn(*mut u8, u32, *mut c_void),
    user_data: *mut c_void,
}
unsafe impl Send for CallbackData {}
unsafe impl Sync for CallbackData {}

impl CallbackData {
    unsafe fn call(&self, data: &mut [u8]) {
        (self.callback)(data.as_mut_ptr(), data.len() as u32, self.user_data);
    }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn ohmw_node_create(path: *const c_char, path_len: u32, kind: NodeKind) -> *mut c_void {
    let create = || {
        let node_path_slice = unsafe { std::slice::from_raw_parts(path as *const u8, path_len as usize) };
        let node_path = std::str::from_utf8(node_path_slice)?;
        Ok::<Node, Box<dyn Error>>(Node::new(node_path, kind)?)
    };
    let node = match create() {
        Ok(node) => node,
        Err(err) => {
            tracing::error!("{err:?}");
            return std::ptr::null_mut()
        },
    };
    Box::into_raw(Box::new(node)) as *mut c_void
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn ohmw_node_destroy(node: *mut c_void) {
    let node = unsafe { Box::from_raw(node as *mut Node) };
    drop(*node);
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn ohmw_node_send(
    node: *mut c_void,
    path: *const c_char,
    path_len: u32,
    data: *const u8,
    data_len: u32,
) -> i32 {
    let send = || {
        let path_slice = unsafe { std::slice::from_raw_parts(path as *const u8, path_len as usize) };
        let path = std::str::from_utf8(path_slice)?;
        let data_slice = unsafe { std::slice::from_raw_parts(data, data_len as usize) };
        let node = Box::leak(unsafe { Box::from_raw(node as *mut Node) });
        Ok::<(), Box<dyn Error>>(node.send(path, data_slice)?)
    };
    match send() {
        Ok(_) => 0,
        Err(err) => {
            tracing::error!("{err:?}");
            -1
        },
    }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn ohmw_node_recv(
    node: *mut c_void,
    path: *const c_char,
    path_len: u32,
    data: *mut u8,
    data_len: *mut u32,
    timeout: u32,
) -> i32 {
    let recv = || {
        let path_slice = unsafe { std::slice::from_raw_parts(path as *const u8, path_len as usize) };
        let path = std::str::from_utf8(path_slice)?;
        let node = Box::leak(unsafe { Box::from_raw(node as *mut Node) });
        Ok::<Vec<u8>, Box<dyn Error>>(node.recv(path, timeout)?)
    };
    match recv() {
        Ok(res) => {
            if res.len() > unsafe { *data_len } as usize {
                tracing::error!("failed to recv node: data buffer is too small");
                return -2;
            }
            unsafe { std::ptr::copy_nonoverlapping(res.as_ptr(), data, res.len()) };
            unsafe { *data_len = res.len() as u32 };
            0
        },
        Err(err) => {
            tracing::error!("{err:?}");
            -1
        },
    }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn ohmw_node_on_recv(
    node: *mut c_void,
    path: *const c_char,
    path_len: u32,
    callback: extern "C" fn(*mut u8, u32, *mut c_void),
    user_data: *mut c_void,
) -> i32 {
    let on_recv = || {
        let path_slice = unsafe { std::slice::from_raw_parts(path as *const u8, path_len as usize) };
        let path = std::str::from_utf8(path_slice)?;
        let node = Box::leak(unsafe { Box::from_raw(node as *mut Node) });
        let callback_data = CallbackData { callback, user_data };
        let callback = move |data: &mut [u8]| {
            unsafe { callback_data.call(data) };
        };
        Ok::<(), Box<dyn Error>>(node.on_recv(path, callback)?)
    };
    match on_recv() {
        Ok(_) => 0,
        Err(err) => {
            tracing::error!("{err:?}");
            -1
        },
    }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn ohmw_node_channel(
    node: *mut c_void,
    path: *const c_char,
    path_len: u32,
) -> i32 {
    todo!()
}
